
import { linear, calc } from './index';

class KeyFrame {
	/**
	 * @param {number} time
	 * @param {number} value
	 * @param {(i:number)=>number} easing
	 * @param {(p:{time:number,value:number,done:function})=>void} callback
	 */
	constructor(time, value, easing = linear, callback = null) {
		this.time = time;
		this.value = value;
		this.easing = easing;
		this.callback = callback;
		this.done = false;
	}
}

/**
 * 仅支持 time 逐渐提高的访问, 不支持随机时间访问
 * 不再支持 name
 */
class KeyFrameSequence {
	/** @type {KeyFrame[]} */ keys = [];

	/**
	 * 当前所处的位置
	 * 0 表示比第一帧还早
	 * =seq.length 表示比最后一帧晚
	 */
	currentIndex = 0;
	done = false;
	/** @type {(p:{time:number,value:number})=>void} */
	listener = null;

	/**
	 * @param {number} lastValue 
	 */
	constructor(lastValue = null) {
		this.lastValue = lastValue;
		this.lastTime = 0;
	}

	/**
	 * @param {(p:{time:number,value:number,done:function})=>void} onValueUpdated
	 *   每当 value 的值变化就回调.
	 *   如果调用了 done, 则认为触发结束, 后面的关键帧以及 onValueUpdated 不再触发
	 */
	whenUpdated(onValueUpdated = null) {
		this.listener = onValueUpdated;
		return this;
	}

	update() {
		/** @type {KeyFrame[]} */ const readyToAdds = [];
		/** @type {KeyFrame} */ let last;
		let obj = {
			when : (/** @type {number} */ time, /** @type {number} */ value) => {
				last = new KeyFrame(time, value);
				readyToAdds.push(last);
				return obj;
			},
			easing : (/** @type {(i:number)=>number} */ easingFunc) => {
				if (last) last.easing = easingFunc;
				return obj;
			},
			callback : (/** @type {(p:{time:number,value:number,done:function})=>void} */ func) => {
				if (last) last.callback = func;
				return obj;
			},
			commit : () => {
				this.keys.push(...readyToAdds);
				this.keys.sort((a, b) => a.time - b.time);
				this.done = false;
				this.calculateIndex();
				return this;
			}
		};
		return obj;
	}

	clear() {
		this.keys = [];
	}

	resetTime(lastValue = null) {
		this.currentIndex = 0;
		this.lastValue = lastValue;
		this.lastTime = 0;
	}

	calculateIndex() {
		const {keys} = this;

		let i = this.currentIndex;
		for (; i < keys.length; i++) {
			let frame = keys[i];
			if (!frame.done) {
				break;
			}
		}

		this.currentIndex = i;
	}

	/**
	 * @param {number} deltaTime
	 * 
	 * tick 的时候, 各个关键帧的 callback 先触发, 随后全局的 listener 再触发
	 */
	tick(deltaTime) {
		const toTime = this.lastTime + deltaTime;
		this.lastTime = toTime;

		if (this.done)
			return this.lastValue;

		const {keys} = this;
		if (this.currentIndex == keys.length) {
			this.done = true;
			return keys.length == 0 ? this.lastValue : keys[keys.length - 1].value;
		}

		/** @type {((p:{time:number,value:number,done:function})=>void)[]} */
		const callbacks = [];
		let i = this.currentIndex;
		for (; i < keys.length; i++) {
			let frame = keys[i];
			if (frame.time > toTime || frame.done) {
				break;
			}
			frame.done = true;
			if (frame.callback) {
				callbacks.push(frame.callback);
			}
		}

		let value = 0;
		if (i == 0) {
			// this.currentIndex is 0;
			value = keys[0].value;
		} else if (i == keys.length) {
			this.currentIndex = keys.length;
			value = keys[i - 1].value;
		} else {
			this.currentIndex = i;
			const startTime = keys[i - 1].time;
			const endTime = keys[i].time;
			value = (startTime == endTime) ? keys[i].value :
				calc(keys[i - 1].value, keys[i].value,
					(toTime - startTime) / (endTime - startTime), keys[i].easing);
		}

		if (this.lastValue != value) {
			this.lastValue = value;
			this.listener && callbacks.push(this.listener);
		}

		let done = this.done;
		callbacks.forEach(c => done || c({time : toTime, value, done : () => done = true}));
		this.done = done;
		return value;
	}

	forceToEnd() {
		const { keys } = this;
		if (keys.length && !this.done) {
			this.tick(Math.max(keys[keys.length - 1].time - this.lastTime, 0));
		}
		this.done = true;
	}
}

export {
	KeyFrame, KeyFrameSequence,
};
